#include "kernel.h"
#include "tju_packet.h"
#include "global.h"

// 初始化全局文件指针
FILE* debug_log_file = NULL;
FILE* trace_log_file = NULL;

// 获取当前UTC时间戳（精确到微秒）
long getCurrentTime(){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000 + tv.tv_usec;
}
/*
模拟Linux内核收到一份TCP报文的处理函数
*/
void onTCPPocket(char* pkt){
    // 当我们收到TCP包时 包中 源IP 源端口 是发送方的 也就是我们眼里的 远程(remote) IP和端口
    uint16_t remote_port = get_src(pkt);
    uint16_t local_port = get_dst(pkt);
    // 获取数据包的序列号和确认号，用于调试
    uint32_t seq = get_seq(pkt);
    uint32_t ack = get_ack(pkt);
    uint8_t flags = get_flags(pkt);
    
    // remote ip 和 local ip 是读IP 数据包得到的 仿真的话这里直接根据hostname判断
    char hostname[8];
    gethostname(hostname, 8);
    uint32_t remote_ip, local_ip;
    if(strcmp(hostname,"server")==0){ // 自己是服务端 远端就是客户端
        local_ip = inet_network(SERVER_IP);
        remote_ip = inet_network(CLIENT_IP);
        // 修复IP地址显示问题，直接使用inet_addr获取网络字节序的IP地址
        struct in_addr local_in_addr, remote_in_addr;
        local_in_addr.s_addr = inet_addr(SERVER_IP);
        remote_in_addr.s_addr = inet_addr(CLIENT_IP);
    }else if(strcmp(hostname,"client")==0){ // 自己是客户端 远端就是服务端 
        local_ip = inet_network(CLIENT_IP);
        remote_ip = inet_network(SERVER_IP);
        // 修复IP地址显示问题，直接使用inet_addr获取网络字节序的IP地址
        struct in_addr local_in_addr, remote_in_addr;
        local_in_addr.s_addr = inet_addr(CLIENT_IP);
        remote_in_addr.s_addr = inet_addr(SERVER_IP);
    }

    int hashval;
    // 根据4个ip port 组成四元组 查找有没有已经建立连接的socket
    hashval = cal_hash(local_ip, local_port, remote_ip, remote_port);

    // 首先查找已经建立连接的socket哈希表
    if (established_socks[hashval]!=NULL){
        tju_handle_packet(established_socks[hashval], pkt);
        return;
    }

    // 没有的话再查找监听中的socket哈希表
    hashval = cal_hash(local_ip, local_port, 0, 0); //监听的socket只有本地监听ip和端口 没有远端
    
    if (listen_socks[hashval]!=NULL){
        tju_handle_packet(listen_socks[hashval], pkt);
        return;
    }

    // 都没找到 丢掉数据包
    return;
}



/*
以用户填写的TCP报文为参数
根据用户填写的TCP的目的IP和目的端口,向该地址发送数据报
不可以修改此函数实现
*/
void sendToLayer3(char* packet_buf, int packet_len){
    if (packet_len>MAX_LEN){
        DEBUG_STATE("ERROR: 不能发送超过 MAX_LEN 长度的packet, 防止IP层进行分片，当前packet长度为 %d", packet_len);
        return;
    }

    // 获取hostname 根据hostname 判断是客户端还是服务端
    char hostname[8];
    gethostname(hostname, 8);
    
    // 解析数据包信息用于调试和trace
    uint32_t seq = get_seq(packet_buf);
    uint32_t ack = get_ack(packet_buf);
    uint8_t flags = get_flags(packet_buf);
    uint16_t src_port = get_src(packet_buf);
    uint16_t dst_port = get_dst(packet_buf);
    // 计算payload长度（总长度减去header长度）
    int payload_len = packet_len - DEFAULT_HEADER_LEN;
    
    // 转换flag为数字表示
    int flag_num = 0;
    if (flags & ACK_FLAG_MASK) flag_num += 4;
    if (flags & SYN_FLAG_MASK) flag_num += 8;
    if (flags & FIN_FLAG_MASK) flag_num += 2;
    
    // 记录TRACE日志
    TRACE_SEND(seq, ack, flag_num, payload_len);
    
    DEBUG_PACKET("[发送] 准备发送一个 %d 字节的数据包", packet_len);
    DEBUG_PACKET("[发送] 源端口: %d, 目的端口: %d", src_port, dst_port);
    DEBUG_PACKET("[发送] 序列号: %u, 确认号: %u", seq, ack);
    DEBUG_PACKET("[发送] TCP标志位: %s%s%s",
    (flags & FIN_FLAG_MASK) ? "FIN " : "    ",
    (flags & SYN_FLAG_MASK) ? "SYN " : "    ",
    (flags & ACK_FLAG_MASK) ? "ACK " : "    ");

    struct sockaddr_in conn;
    conn.sin_family      = AF_INET;            
    conn.sin_port        = htons(20218);
    int rst;
    if(strcmp(hostname,"server")==0){
        conn.sin_addr.s_addr = inet_addr(CLIENT_IP);
        rst = sendto(BACKEND_UDPSOCKET_ID, packet_buf, packet_len, 0, (struct sockaddr*)&conn, sizeof(conn));
    }else if(strcmp(hostname,"client")==0){       
        conn.sin_addr.s_addr = inet_addr(SERVER_IP);
        rst = sendto(BACKEND_UDPSOCKET_ID, packet_buf, packet_len, 0, (struct sockaddr*)&conn, sizeof(conn));
    }else{
        DEBUG_STATE("请不要改动hostname...");
        exit(-1);
    }
    
    if(rst < 0) {
        DEBUG_STATE("发送失败: %d", rst);
    } else {
        DEBUG_DETAIL("发送成功，实际发送字节数: %d", rst);
    }
}

/*
 仿真接受数据线程
 不断调用server或cliet监听在20218端口的UDPsocket的recvfrom
 一旦收到了大于TCPheader长度的数据 
 则接受整个TCP包并调用onTCPPocket()
*/
void* receive_thread(void* arg){

    char hdr[DEFAULT_HEADER_LEN];
    char* pkt;

    uint32_t plen = 0, buf_size = 0, n = 0;
    int len;

    struct sockaddr_in from_addr;
    int from_addr_size = sizeof(from_addr);
    
    int i = 1; // 用于调试 记录接收次数

    while(1) {
        DEBUG_DETAIL("第 %d 次接收数据", i);
        
        // MSG_PEEK 表示看一眼 不会把数据从缓冲区删除
        len = recvfrom(BACKEND_UDPSOCKET_ID, hdr, DEFAULT_HEADER_LEN, MSG_PEEK, (struct sockaddr *)&from_addr, &from_addr_size);
        // 一旦收到了大于header长度的数据 则接受整个TCP包
        if(len >= DEFAULT_HEADER_LEN){
            plen = get_plen(hdr); 
            pkt = malloc(plen);
            buf_size = 0;
            while(buf_size < plen){ // 直到接收到 plen 长度的数据 接受的数据全部存在pkt中
                n = recvfrom(BACKEND_UDPSOCKET_ID, pkt + buf_size, plen - buf_size, NO_FLAG, (struct sockaddr *)&from_addr, &from_addr_size);
                buf_size = buf_size + n;
            }
            
            // 解析序列号和确认号，用于调试和trace
            uint32_t seq = get_seq(pkt);
            uint32_t ack = get_ack(pkt);
            uint8_t flags = get_flags(pkt);
            uint16_t src_port = get_src(pkt);
            uint16_t dst_port = get_dst(pkt);
            
            // 计算payload长度（总长度减去header长度）
            int payload_len = buf_size - DEFAULT_HEADER_LEN;
            
            // 转换flag为数字表示
            int flag_num = 0;
            if (flags & ACK_FLAG_MASK) flag_num += 4;
            if (flags & SYN_FLAG_MASK) flag_num += 8;
            if (flags & FIN_FLAG_MASK) flag_num += 2;
            
            // 记录TRACE日志
            TRACE_RECV(seq, ack, flag_num, payload_len);
            
            DEBUG_PACKET("[接收] 接收到一个 %d 字节的数据包", buf_size);
            DEBUG_PACKET("[接收] 源端口: %d, 目的端口: %d", src_port, dst_port);
            DEBUG_PACKET("[接收] 序列号: %u, 确认号: %u", seq, ack);
            DEBUG_PACKET("[接收] TCP标志位: %s%s%s",
            (flags & FIN_FLAG_MASK) ? "FIN " : "    ",
            (flags & SYN_FLAG_MASK) ? "SYN " : "    ",
            (flags & ACK_FLAG_MASK) ? "ACK " : "    ");
            
            // 通知内核收到一个完整的TCP报文
            onTCPPocket(pkt);
            free(pkt);
            
            DEBUG_DETAIL("第 %d 次接收数据完成", i);
            i++;
        } else if (len < 0) {
            DEBUG_STATE("接收数据失败，错误码: %d", len);
        }
    }
    DEBUG_DETAIL("ERROR：接收线程函数退出工作\n");
}

/*
 设置trace日志文件
*/
void set_trace_log_file(const char* filename) {
    if (trace_log_file != NULL) {
        fclose(trace_log_file);
    }
    trace_log_file = fopen(filename, "w"); // 覆盖模式
    if (trace_log_file == NULL) {
        printf("Error opening trace log file: %s\n", filename);
    }
}

/*
 关闭trace日志文件
*/
void close_trace_log_file() {
    if (trace_log_file != NULL) {
        fclose(trace_log_file);
        trace_log_file = NULL;
    }
}

/*
 开启仿真, 运行起后台线程

 不论是server还是client
 都创建一个UDP socket 监听在20218端口
 然后创建新线程 不断调用该socket的recvfrom
*/
void startSimulation(){
    // 对于内核 初始化监听socket哈希表和建立连接socket哈希表
    int index;
    for(index=0;index<MAX_SOCK;index++){
        listen_socks[index] = NULL;
        established_socks[index] = NULL;
    }

    // 获取hostname 
    char hostname[8];
    gethostname(hostname, 8);
    
    // 根据hostname创建对应的trace日志文件
    char trace_filename[100];
    if(strcmp(hostname, "server") == 0) {
        snprintf(trace_filename, sizeof(trace_filename), "/vagrant/tju_tcp/test/server.event.trace");
    } else if(strcmp(hostname, "client") == 0) {
        snprintf(trace_filename, sizeof(trace_filename), "/vagrant/tju_tcp/test/client.event.trace");
    } else {
        snprintf(trace_filename, sizeof(trace_filename), "/vagrant/tju_tcp/test/unknown.event.trace");
    }
    set_trace_log_file(trace_filename);
    
    // printf("startSimulation on hostname: %s\n", hostname);

    BACKEND_UDPSOCKET_ID = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (BACKEND_UDPSOCKET_ID < 0){
        DEBUG_STATE("ERROR opening socket");
        exit(-1);
    }

    // 设置socket选项 SO_REUSEADDR = 1 
    // 意思是 允许绑定本地地址冲突 和 改变了系统对处于TIME_WAIT状态的socket的看待方式 
    int optval = 1;
    setsockopt(BACKEND_UDPSOCKET_ID, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));

    struct sockaddr_in conn;
    memset(&conn, 0, sizeof(conn)); 
    conn.sin_family = AF_INET;
    conn.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY = 0.0.0.0
    conn.sin_port = htons((unsigned short)20218);

    if (bind(BACKEND_UDPSOCKET_ID, (struct sockaddr *) &conn, sizeof(conn)) < 0){
        DEBUG_STATE("ERROR on binding");
        exit(-1);
    }

    pthread_t thread_id = 1001;
    int rst = pthread_create(&thread_id, NULL, receive_thread, (void*)(&BACKEND_UDPSOCKET_ID));
    if (rst<0){
        DEBUG_STATE("ERROR open thread");
        exit(-1); 
    }
    // printf("successfully created bankend thread\n");
    return;
}

int cal_hash(uint32_t local_ip, uint16_t local_port, uint32_t remote_ip, uint16_t remote_port){
    // 计算哈希值
    uint32_t sum = (uint32_t)local_ip + (uint32_t)local_port + (uint32_t)remote_ip + (uint32_t)remote_port;
    int hashval = sum % MAX_SOCK;
    
    return hashval;
}